"""
扫码枪监控模块
监控PLC扫码枪变量变化，当检测到包含两个"-"的字符串时发送WebSocket消息
"""
import asyncio
import threading
import time
import json
from datetime import datetime
from typing import Optional

from plc_config import SCANNER_BOLCK_VARIABLES, DATA_BLOCK_VARIABLES
from plc_client import PLCClient
from websocket_manager import websocket_publish


class ScannerMonitor:
    """扫码枪监控器"""
    
    def __init__(self, plc_client: PLCClient, sim_mode: bool = False):
        self.plc_client = plc_client
        self.sim_mode = sim_mode
        self.running = False
        self.monitor_thread = None
        self.last_scanner_value = ""
        self.monitor_interval = 1.0  # 1秒间隔
        # self.is_first_read = True  # 标记是否为第一次读取
        
    @property
    def is_monitoring(self):
        """检查是否正在监控"""
        return self.running and self.monitor_thread and self.monitor_thread.is_alive()
        
    def start_monitoring(self):
        """启动监控线程"""
        if self.running:
            return
            
        self.running = True
        self.monitor_thread = threading.Thread(target=self._monitor_loop, daemon=True)
        self.monitor_thread.start()
        
    def stop_monitoring(self):
        """停止监控线程"""
        self.running = False
        if self.monitor_thread and self.monitor_thread.is_alive():
            self.monitor_thread.join(timeout=2)
        
    def _monitor_loop(self):
        """监控循环"""
        
        while self.running:
            try:
                # 读取扫码枪变量
                current_value = self._read_scanner_variable()
                
                # # 如果是第一次读取，只记录值，不发送更新
                # if self.is_first_read:
                #     self.last_scanner_value = current_value
                #     self.is_first_read = False
                #     print(f"[扫码枪监控] 记录初始扫码枪值: '{current_value}'")
                #     time.sleep(self.monitor_interval)
                #     continue
                
                # 检查是否有变化
                if current_value != self.last_scanner_value:
                    print(f"[扫码枪监控] 检测到变化: '{self.last_scanner_value}' -> '{current_value}'")
                    
                    # 检查是否包含一个"-"（追溯条码格式）
                    if self._is_valid_trace_barcode(current_value):
                        print(f"[扫码枪监控] 检测到有效追溯条码: {current_value}")
                        
                        # 更新追溯条码到PLC数据块
                        self._update_plc_field("trace_barcode", current_value)
                        # 发送追溯条码WebSocket消息
                        self._send_field_update("trace_barcode", current_value)
                        
                        # 建立追溯条码到项目名称的映射
                        project_name = self._map_trace_barcode_to_project_name(current_value)
                        if project_name:
                            self._update_plc_field("project_name", project_name)
                            self._send_field_update("project_name", project_name)
                        else:
                            try:
                                project_code = str(current_value).split('-')[0]
                                print(f"[扫码枪监控] 未配置的项目编号: {project_code}，广播提示事件")
                                websocket_publish(json.dumps({"type":"project_not_configured","timestamp":datetime.now().isoformat(),"project_code":project_code}))
                            except Exception:
                                pass
                    else:
                        print(f"[扫码枪监控] 字符串格式不符合要求: {current_value}")
                    
                    # 更新上次的值
                    self.last_scanner_value = current_value
                
                # 等待下次检查
                time.sleep(self.monitor_interval)
                
            except Exception as e:
                print(f"[扫码枪监控] 监控过程中出错: {str(e)}")
                time.sleep(self.monitor_interval)
                
    def _read_scanner_variable(self) -> str:
        """读取扫码枪变量值"""
        try:
            if self.sim_mode:
                # 模拟模式：返回模拟数据
                import random
                test_values = [
                    "",  # 空值
                    "TEST-001-ABC",  # 有效格式
                    "INVALID",  # 无效格式
                    "MOTOR-123-DEF",  # 有效格式
                    "NO-DASH",  # 无效格式
                ]
                # 大部分时间返回空值，偶尔返回测试值
                if random.random() < 0.05:  # 5%概率返回测试值
                    return random.choice(test_values[1:])
                return ""
            else:
                # 实际模式：从PLC读取
                if not self.plc_client or not self.plc_client.connected:
                    return ""
                    
                # 获取扫码枪变量配置
                scanner_var = SCANNER_BOLCK_VARIABLES.get("ScannerString")
                if not scanner_var:
                    print("[扫码枪监控] 未找到扫码枪变量配置")
                    return ""
                
                # 读取PLC数据
                value = self.plc_client.read_data_block_variable(scanner_var)
                return str(value) if value is not None else ""
                
        except Exception as e:
            print(f"[扫码枪监控] 读取扫码枪变量失败: {str(e)}")
            return ""
            
    def _is_valid_trace_barcode(self, value: str) -> bool:
        """检查字符串是否包含一个"-"符号（追溯条码格式）"""
        if not value or not isinstance(value, str):
            return False
        return value.count("-") >= 1
        
    def _map_trace_barcode_to_project_name(self, trace_barcode: str) -> str:
        if not trace_barcode:
            return ""
        try:
            project_code = str(trace_barcode).split('-')[0]
            from projects import load_projects
            projects = load_projects()
            for p in projects:
                if str(p.get('项目编号', '')).strip() == project_code.strip():
                    return str(p.get('项目名称', ''))
            return ""
        except Exception:
            return ""
        
    def _send_field_update(self, field_name: str, field_value: str):
        """发送字段更新的WebSocket消息"""
        try:
            # 构造WebSocket消息
            message = {
                "type": "field_update",
                "timestamp": datetime.now().isoformat(),
                "field_name": field_name,
                "field_value": field_value
            }
            
            # 发送WebSocket消息
            websocket_publish(json.dumps(message))
            print(f"[扫码枪监控] 已发送{field_name}更新消息: {field_value}")
            
        except Exception as e:
            print(f"[扫码枪监控] 发送{field_name}WebSocket消息失败: {str(e)}")
    
    def _update_plc_field(self, field_name: str, value: str) -> bool:
        """
        更新PLC数据块中的字段值
        
        Args:
            field_name: 字段名称
            value: 要写入的值
            
        Returns:
            是否更新成功
        """
        try:
            # 从配置中查找字段信息
            field_config = DATA_BLOCK_VARIABLES.get(field_name)
            if not field_config:
                print(f"[扫码枪监控] 未找到字段 {field_name} 的配置信息，跳过PLC更新")
                return False
            
            # 如果是模拟模式，只打印日志不实际写入
            if self.sim_mode:
                print(f"[扫码枪监控] 模拟模式：更新PLC字段 {field_name} = '{value}' (DB{field_config.db_number}.{field_config.start_offset})")
                return True
            
            # 检查PLC连接状态
            if not self.plc_client or not self.plc_client.connected:
                print(f"[扫码枪监控] PLC未连接，无法更新字段 {field_name}")
                return False
            
            # 根据数据类型转换值并写入PLC
            try:
                # 获取数据类型
                data_type = field_config.data_type
                
                # 根据数据类型转换值
                if data_type.name in ["STRING", "WSTRING"]:
                    converted_value = str(value)
                elif data_type.name in ["CHAR", "WCHAR"]:
                    converted_value = str(value)[:1] if value else ""  # 单字符
                elif data_type.name in ["INT", "SINT", "DINT", "LINT"]:
                    converted_value = int(value)
                elif data_type.name in ["UINT", "USINT", "UDINT", "ULINT"]:
                    converted_value = abs(int(value))  # 无符号整数，取绝对值
                elif data_type.name in ["BYTE", "WORD", "DWORD", "LWORD"]:
                    converted_value = abs(int(value))  # 无符号数据
                elif data_type.name in ["REAL", "LREAL"]:
                    converted_value = float(value)
                elif data_type.name == "BOOL":
                    converted_value = value.lower() in ['true', '1', 'yes', 'on'] if isinstance(value, str) else bool(value)
                elif data_type.name in ["TIME", "LTIME", "S5TIME", "DATE", "TIME_OF_DAY", "TOD", "DATE_AND_TIME", "DT"]:
                    # 时间类型通常以毫秒或其他时间单位表示
                    converted_value = int(value)
                elif data_type.name in ["COUNTER", "TIMER"]:
                    # 计数器和定时器类型
                    converted_value = int(value)
                else:
                    print(f"[扫码枪监控] 不支持的数据类型: {data_type.name}")
                    return False
                
                # 使用正确的PLCClient方法写入数据块变量
                success = self.plc_client.write_data_block_variable(field_config, converted_value)
                
            except ValueError as e:
                print(f"[扫码枪监控] 数据类型转换失败: {e}")
                return False
            
            if success:
                print(f"[扫码枪监控] 成功更新PLC字段 {field_name} = '{value}' (DB{field_config.db_number}.{field_config.start_offset})")
                return True
            else:
                print(f"[扫码枪监控] 更新PLC字段 {field_name} 失败")
                return False
                
        except Exception as e:
            print(f"[扫码枪监控] 更新PLC字段 {field_name} 时出错: {str(e)}")
            return False


# 全局监控器实例和控制标志
scanner_monitor: Optional[ScannerMonitor] = None
scanner_ready_flag: bool = False  # 前端就绪标志


def start_scanner_monitoring(plc_client: PLCClient, sim_mode: bool = False):
    """启动扫码枪监控（仅在前端就绪时开始实际监控）"""
    global scanner_monitor, scanner_ready_flag
    
    if scanner_monitor:
        scanner_monitor.stop_monitoring()
    
    scanner_monitor = ScannerMonitor(plc_client, sim_mode)
    
    # 只有在前端就绪时才开始监控
    if scanner_ready_flag:
        scanner_monitor.start_monitoring()
        print("[扫码枪监控] 前端已就绪，开始监控")
    else:
        print("[扫码枪监控] 等待前端就绪信号...")


def set_scanner_ready(ready: bool = True):
    """设置扫码枪监控就绪状态"""
    global scanner_monitor, scanner_ready_flag
    
    scanner_ready_flag = ready
    
    if ready and scanner_monitor and not scanner_monitor.is_monitoring:
        scanner_monitor.start_monitoring()
    elif not ready and scanner_monitor and scanner_monitor.is_monitoring:
        scanner_monitor.stop_monitoring()


def get_scanner_ready_status():
    """获取扫码枪监控就绪状态"""
    global scanner_ready_flag
    return scanner_ready_flag
    

def stop_scanner_monitoring():
    """停止扫码枪监控"""
    global scanner_monitor
    
    if scanner_monitor:
        scanner_monitor.stop_monitoring()
        scanner_monitor = None


if __name__ == "__main__":
    """单元测试"""
    import sys
    import os
    
    # 添加当前目录到Python路径，确保能导入其他模块
    sys.path.append(os.path.dirname(os.path.abspath(__file__)))
    
    # 模拟PLC配置
    test_plc_config = {
        'host': '192.168.1.1',
        'rack': 0,
        'slot': 1,
        'timeout': 5  # 添加缺失的timeout参数
    }
    
    sim_mode=False
    
    print("=" * 60)
    print("扫码枪监控模块单元测试")
    print("=" * 60)
    
    # 创建模拟PLC客户端
    print("1. 创建模拟PLC客户端...")
    mock_plc_client = PLCClient(test_plc_config)
    
    # 测试扫码枪监控器初始化
    print("2. 初始化扫码枪监控器...")
    test_monitor = ScannerMonitor(mock_plc_client, sim_mode)
    
    # 测试字符串验证功能
    print("3. 测试字符串验证功能...")
    test_cases = [
        ("", False, "空字符串"),
        ("TEST", False, "无横线"),
        ("TEST-001", True, "一个横线（有效追溯条码）"),
        ("TEST-001-ABC", True, "两个横线（有效追溯条码）"),
        ("MOTOR-123-DEF-GHI", True, "三个横线（有效追溯条码）"),
        ("NO-DASH", True, "一个横线（有效追溯条码）"),
        ("NODASH", False, "无横线"),
        (None, False, "None值"),
        (123, False, "非字符串类型")
    ]
    
    for test_value, expected, description in test_cases:
        result = test_monitor._is_valid_trace_barcode(test_value)
        status = "✅ 通过" if result == expected else "❌ 失败"
        print(f"   {status} {description}: '{test_value}' -> {result}")
    
    # 测试扫码枪变量读取（模拟模式）
    print("4. 测试扫码枪变量读取（模拟模式）...")
    for i in range(5):
        value = test_monitor._read_scanner_variable()
        print(f"   读取 #{i+1}: '{value}'")
    
    # 测试监控启动和停止
    print("5. 测试监控启动和停止（包含初始化逻辑）...")
    print("   启动监控...")
    test_monitor.start_monitoring()
    
    print("   监控运行中（3秒，观察初始化逻辑）...")
    time.sleep(3)  # 缩短时间
    
    print("   停止监控...")
    test_monitor.stop_monitoring()
    
    # 测试全局函数
    print("6. 测试全局监控函数...")
    print("   启动全局监控...")
    start_scanner_monitoring(mock_plc_client, sim_mode=True)
    
    time.sleep(3)
    
    print("   停止全局监控...")
    stop_scanner_monitoring()
    
    print("=" * 60)
    print("单元测试完成！")
    print("=" * 60)